Documentation v2.0 — Mai 2026
Nouveautés, modes de jeu, social, profil & notes techniques
v2.0 Documentation — May 2026
What's new, game modes, social features, profile & technical notes
Avant
Before
Après
After
La v2.0 passe d'un jeu localStorage uniquement à une plateforme complète : backend PHP/MariaDB, comptes utilisateurs, social, et infrastructure CI/CD.
v2.0 transforms PersonaDLE from a localStorage-only game into a full platform: PHP/MariaDB backend, user accounts, social features, and CI/CD infrastructure.
EN · FR · ES · DE · IT — 940+ keysEN · FR · ES · DE · IT — 940+ clés
PersonaDLE propose 6 modes quotidiens, chacun avec un défi différent basé sur les personnages de la saga Persona (P1 → P5X).
PersonaDLE offers 6 daily modes, each with a different challenge based on Persona series characters (P1 → P5X).
Devinez le personnage du jour en comparant ses attributs : nom, genre, âge, persona, arcane, opus. Chaque tentative révèle des indices par couleur.
Guess the daily character by comparing attributes: name, gender, age, persona, arcana, opus. Each attempt reveals color-coded hints.
Identifiez le personnage à partir d'une série d'emojis qui représentent sa personnalité, ses attributs ou son histoire.
Identify the character from a series of emojis representing their personality, attributes, or story.
Reconnaissez le personnage depuis sa silhouette, qui se révèle progressivement à chaque tentative manquée.
Recognize the character from their silhouette, which gradually reveals itself with each missed attempt.
Identifiez le personnage depuis son animation d'All-Out Attack floue, qui devient plus nette à chaque tentative.
Identify the character from their blurred All-Out Attack animation, which sharpens with each attempt.
Devinez le personnage en voyant uniquement ses Personas. La difficulté augmente progressivement.
Guess the character by seeing only their Personas. Difficulty increases progressively.
Identifiez la chanson jouée dans le lecteur audio. 130+ pistes couvrant P1 → P5X, filtrables par opus.
Identify the song playing in the audio player. 130+ tracks covering P1 → P5X, filterable by opus.
Sub-filters per game (P3, P4, P5…)Sous-filtres par jeu (P3, P4, P5…)
Antagonistes (P3, P4, P5) — Les boss finaux sont maintenant jouables dans les modes compatibles :
Antagonists (P3, P4, P5) — Final bosses are now playable in compatible modes:
Persona 5 Strikers — 6 nouveaux personnages :
Persona 5 Strikers — 6 new characters:
| Portrait | Portrait | Character | Personnage | Category | Catégorie | Shadow / Persona |
|---|---|---|---|---|---|---|
![]() |
Akane Hasegawa | Secondary | Secondaire | Shadow Akane | ||
![]() |
Kuon Ichinose | Secondary | Secondaire | — | ||
![]() |
Alice Hiiragi | Antagonist — Jail Ruler | Antagoniste — Jail Ruler | Mad Rabbit Alice | ||
![]() |
Ango Natsume | Antagonist — Jail Ruler | Antagoniste — Jail Ruler | Nightmare Dragon Ango | ||
![]() |
Mariko Hyodo | Antagonist — Jail Ruler | Antagoniste — Jail Ruler | Snow Empress Mariko | ||
![]() |
Akira Konoe | Main Antagonist (BAAL) | Antagoniste principal (BAAL) | Akira the Hero |
Persona 5: The Phantom X — New Phantom Idols: playable across Classic, Silhouette and Personae.
Persona 5: The Phantom X — Nouveaux Phantom Idols : jouables en Classique, Silhouette et Personae.
Gentileschi
Asterope
Kiskil-lilla
Joker Starlight joins the All-Out Attack roster — a Dancing in Starlight variant with a unique animated finish screen.
Joker Starlight rejoint le mode All-Out Attack — une variante Dancing in Starlight avec un écran de fin animé unique.
Joker Starlight
Ren Amamiya — Persona 5 X
Mona Starlight
Morgana — Persona 5 X
Three limited Summer event characters, each with a distinct beach-themed All-Out Attack animation.
Trois personnages limités de l'événement Summer, chacun avec une animation All-Out Attack sur le thème de la plage.
Wonder Summer
Nagisa Kamishiro — Persona 5 X
Closer Summer
Motoha Arai — Persona 5 X
Moko Summer
Tomoko Noge — Persona 5 X
Marian Summer
Minami Miyashita — Persona 5 X
Puppet Summer
Miyu Sahara — Persona 5 X
Closer Radiance
Motoha Arai — Persona 5 X
Anri
Aran Hirano — Persona 5 X
Pinky
Narumi Nashimoto — Persona 5 X
Blitz
Kumi Katayama — Persona 5 X
The virtual diva crosses over into Persona 5 X! Hatsune Miku joins the All-Out Attack roster with a vivid cyan-pink-red finish — her real name behind the alias "Miku", just like Joker is Ren Amamiya.
La diva virtuelle débarque dans Persona 5 X ! Hatsune Miku rejoint le mode All-Out Attack avec un final cyan-rose-rouge éclatant — son vrai nom derrière l'alias « Miku », comme Joker est Ren Amamiya.
Miku
Hatsune Miku — Persona 5 X

P3P
P3R
P4AU
P4G
P5R
P5S
P5TCouvertures disponibles pour 20 opus (P1 → P5X, PQ/PQ2, Velvet, Zutomayo). P4AU et P5T ajoutés en tant que nouveaux filtres dans le panneau de sélection.
Covers available for 20 opus (P1 → P5X, PQ/PQ2, Velvet, Zutomayo). P4AU and P5T added as new filter options in the selection panel.
Inspiré des jeux Persona, chaque relation entre amis progresse en rang (1 → 10) via des interactions mutuelles. Les actions mutuelles donnent 2× l'XP. Anti-spam : une action par type par jour par lien.
Inspired by the Persona games, each friendship progresses in rank (1 → 10) through mutual interactions. Mutual actions grant 2× XP. Anti-spam: one action per type per day per link.
| Rang / Rank | Nom / Name | XP |
|---|---|---|
| 1 | Stranger | 0 |
| 2 | Acquaintance | 100 |
| 3 | Companion | 250 |
| 4 | Ally | 450 |
| 5 | Confidant | 700 |
| 6 | Trusted Ally | 1 000 |
| 7 | True Ally | 1 350 |
| 8 | Bond | 1 750 |
| 9 | Unbreakable Bond | 2 200 |
| 10 | True Confidant ✦ | 2 700 |
Au rang 10 (True Confidant), un effet visuel spécial s'active : halo doré pulsant autour de l'avatar, icône ✦ et label animé.
At rank 10 (True Confidant), a special visual effect activates: pulsing gold halo around the avatar, ✦ icon, and animated label.
?view=FRIENDCODE (lecture seule)?view=FRIENDCODE (read-only)60 badges organisés en 5 catégories : Achievements, Events, Secrets, Social, Streak. Les conditions sont vérifiées côté serveur. Les badges secrets affichent "???" comme condition tant qu'ils ne sont pas débloqués.
60 badges organized into 5 categories: Achievements, Events, Secrets, Social, Streak. Conditions are verified server-side. Secret badges display "???" as their condition until unlocked.
11 titres affichés sous le pseudo. Un seul titre équipé à la fois. Traduits EN/FR/ES/DE/IT.
11 titles displayed below the username. Only one title equipped at a time. Translated in EN/FR/ES/DE/IT.
XMAS2025, NEWYEAR2026, VALENTINE2026…)XMAS2025, NEWYEAR2026, VALENTINE2026…)Cette section est destinée aux développeurs. Elle couvre le stack, l'API, la base de données, la sécurité et le CI/CD.
This section is for developers. It covers the stack, API, database, security, and CI/CD.
| Layer | Couche | Technology | Technologie |
|---|---|---|---|
| Frontend | Frontend | Vanilla JS ES6+, HTML5, CSS3 — zero framework | |
| Backend | Backend | PHP 8.3 + PDO (prepared statements) | |
| Database | Base de données | MySQL 8.0 (local) · MariaDB 10.6+ (Hostinger) | |
| Auth | Auth | bcrypt + PHP httpOnly sessions (no JWT) | |
| Tests | Tests | Vitest + jsdom — 449 tests (npm test) | |
| Hosting | Hébergement | Hostinger — rsync deploy via GitHub Actions | |
Modules clés dans js/ :
Key modules in js/:
| Module | Role | Rôle |
|---|---|---|
gameCore.js | Shared utilities: date (DST-safe Paris), confetti, sessions, filters | Utilitaires partagés : date (DST Paris), confetti, sessions, filtres |
api.js | REST client — exposed as window._personadleApi to avoid circular imports | Client REST — exposé via window._personadleApi pour éviter les imports circulaires |
auth.js | Login/register UI, initAuth(), localStorage → cloud migration | UI login/register, initAuth(), migration localStorage → cloud |
i18n.js | Translation engine: t(key), initLang(), applyToDOM() | Moteur de traduction : t(key), initLang(), applyToDOM() |
cloud-sync.js | Backend is source of truth — pullProfileFromCloud() overwrites localStorage | Backend = source de vérité — pullProfileFromCloud() écrase le localStorage |
social-link.js | XP gauges, rank-up animation, True Confidant effect | Jauges XP, animation rang-up, effet True Confidant |
t(key) returns the raw key (truthy string) when missing — ?? never triggers. Always use:
t(key) retourne la clé brute (string truthy) quand elle est absente — ?? ne se déclenche jamais. Utiliser :
const r = window.i18n?.t?.(key);
return (r != null && r !== key) ? r : fallback;
Fichier : sql/bdd_mysql.sql. Schéma compatible MySQL 8.0 (local) et MariaDB 10.6+ (Hostinger).
File: sql/bdd_mysql.sql. Schema compatible with MySQL 8.0 (local) and MariaDB 10.6+ (Hostinger).
| Table | Description | Description |
|---|---|---|
users | Account (email, pseudo, hash, friend_code, is_banned) | Compte (email, pseudo, hash, friend_code, is_banned) |
profiles | Avatar, wallpaper, equipped badge/title/song | Avatar, fond, badge/titre/chanson équipés |
user_stats | Stats per mode (wins, streak, perfect, time_ms) | Stats par mode (wins, streak, perfect, time_ms) |
game_sessions | Game history (anti-duplicate per user+mode+date) | Historique des parties (anti-duplon user+mode+date) |
badges / badges_unlocked | 60-badge catalog + per-user unlocks | Catalogue 60 badges + déblocages par utilisateur |
wallpapers / user_wallpapers | 7 wallpapers + per-user unlocks | 7 fonds + déblocages par utilisateur |
titles / user_titles | 11 translated titles + per-user unlocks | 11 titres traduits + déblocages par utilisateur |
event_codes / event_codes_redeemed | Event codes (active, dates, badge reward) | Codes événement (actif, dates, récompense badge) |
friendships | Friend requests (pending / accepted / blocked) | Demandes d'amis (pending / accepted / blocked) |
social_links / social_link_interactions | Social Link rank + XP + interaction log | Rang + XP Social Link + log des interactions |
social_link_ranks | XP thresholds for ranks 1-10 | Seuils XP des rangs 1-10 |
social_link_rankup_notifs | Rank-up notifications for both players | Notifications de passage de rang pour les deux joueurs |
messages | Messages and challenges between friends | Messages et défis entre amis |
leaderboard_cache | Cached rankings (recalculated by cron) | Cache des classements (recalculé par cron) |
rate_limits | Anti-abuse counters (login, register, reset) | Compteurs anti-abus (login, register, reset) |
error_log | Application error log (admin panel) | Journal des erreurs applicatives (panel admin) |
admin_audit_log | Log of every admin action (ban, grant badge, etc.) | Log de chaque action admin (ban, don de badge, etc.) |
deletion_requests | GDPR — soft-delete log, hard delete after 30 days | RGPD — log soft-delete, hard delete J+30 |
| Endpoint | Description | Description |
|---|---|---|
POST /api/auth/register | Create account (users + profiles + stats × 6 in one transaction) | Créer un compte (users + profiles + stats × 6 en transaction) |
POST /api/auth/login | Login — dummy hash if email not found (anti-enumeration) | Connexion — hash dummy si email inexistant (anti-énumération) |
GET /api/auth/me | Returns {user: null} if no session (not an error) | Retourne {user: null} si pas de session (pas une erreur) |
POST /api/sessions | Save game session (anti-duplicate, atomic streak calc) | Sauvegarder session de jeu (anti-duplon, calcul streak atomique) |
GET/PATCH/DELETE /api/user/:id | User profile — ownership checks, explicit field whitelist | Profil utilisateur — ownership checks, whitelist explicite des champs |
POST /api/user/migrate | Idempotent localStorage → DB migration (INSERT IGNORE) | Migration idempotente localStorage → BDD (INSERT IGNORE) |
GET /api/leaderboard | Rankings with mode/period/metric/scope filters | Classements avec filtres mode/période/métrique/scope |
GET|POST /api/friends | Friend list, add, respond, remove | Liste amis, ajouter, répondre, supprimer |
GET|POST /api/messages | Messages and challenges CRUD | CRUD messages et défis |
POST /api/social-links/:id/interact | Grant XP with mutual detection (×2) | Accorder XP avec détection mutualité (×2) |
GET /api/badges, /api/wallpapers, /api/titles | Catalogs + condition-verified unlocks | Catalogues + déblocages vérifiés par condition |
GET|POST /api/admin/… | Admin — user management, event codes, moderation | Admin — gestion utilisateurs, codes événement, modération |
GET /api/cron/leaderboard.php | Recalculate 105 leaderboard entries (hourly) | Recalcul 105 entrées leaderboard (toutes les heures) |
GET /api/cron/hard-delete.php | GDPR hard delete J+30 (daily) | Hard delete RGPD J+30 (quotidien) |
| Severity | Sévérité | Issue fixed | Faille corrigée |
|---|---|---|---|
| CRITICAL | Unconditional badge/title/wallpaper unlock — any authenticated account could self-award any item | Unlock inconditionnel badges/titres/fonds — tout compte authentifié pouvait s'auto-attribuer n'importe quel item | |
| HIGH | IDOR on challenge beaten status — challenger could farm 35 XP |
IDOR statut beaten — l'expéditeur d'un défi pouvait se marquer beaten et farmer 35 XP |
|
| HIGH | Banned user not checked on session restore (me.php) |
Utilisateur banni ignoré sur restauration de session (me.php) |
|
| MEDIUM | Missing input validation in api/user/index.php (avatar prefix, slug regex) |
Validation inputs manquante dans api/user/index.php (préfixe avatar, regex slugs) |
|
| MEDIUM | challenge_mode not validated — strict allowlist of 6 valid modes added |
challenge_mode non validé — allowlist stricte des 6 modes valides ajoutée |
|
| LOW | friend_code exposed in leaderboard for unauthenticated visitors |
friend_code exposé dans le leaderboard pour les visiteurs non authentifiés |
|
| LOW | Session fixation — session_regenerate_id(true) added to login/register |
Session fixation — session_regenerate_id(true) ajouté dans login/register |
Rate limiting : 5 tentatives par fenêtre de 15 min par IP sur /api/auth/login et /api/auth/register. Headers de sécurité ajoutés dans api/bootstrap.php : X-Content-Type-Options, X-Frame-Options: DENY, Referrer-Policy.
Rate limiting: 5 attempts per 15-minute window per IP on /api/auth/login and /api/auth/register. Security headers added in api/bootstrap.php: X-Content-Type-Options, X-Frame-Options: DENY, Referrer-Policy.
| Component | Composant | Detail | Détail |
|---|---|---|---|
| Git Hooks | Git Hooks | pre-commit: i18n check + tests. commit-msg: Conventional Commits. pre-push: full test + warn on main push |
pre-commit : i18n + tests. commit-msg : Conventional Commits. pre-push : tests complets + avertissement push main |
| GitHub Actions CI | GitHub Actions CI | On push to main/develop: npm test (449 tests) + i18n:check + PHP lint |
Sur push main/develop : npm test (449 tests) + i18n:check + lint PHP |
| GitHub Actions CD | GitHub Actions CD | Manual only (workflow_dispatch) — rsync to Hostinger via SSH, excludes .git, node_modules, api/config.php, .env |
Manuel uniquement (workflow_dispatch) — rsync vers Hostinger par SSH, exclut .git, node_modules, api/config.php, .env |
| Service Worker | Service Worker | CACHE_VERSION versioning. Network-first for lang/*.json to avoid stale translation cache |
Versioning CACHE_VERSION. Network-first pour lang/*.json — évite le cache de traductions périmé |
| Cron jobs | Cron jobs | Leaderboard cache (hourly) · GDPR hard delete (daily 03:00 Paris) | Cache leaderboard (toutes les heures) · Hard delete RGPD (quotidien 03h00 Paris) |
| Issue | Problème | Fix | Solution |
|---|---|---|---|
t(key) ?? fallback never triggers |
t(key) ?? fallback ne se déclenche jamais |
t() returns raw key (truthy) when missing — use r !== key pattern |
t() retourne la clé (truthy) si absente — utiliser le pattern r !== key |
syncPending blocked on 409 |
syncPending bloqué sur 409 |
409 = already saved — use continue, not return |
409 = déjà enregistré — utiliser continue, pas return |
rank reserved in MySQL 8.0 |
rank mot réservé MySQL 8.0 |
Always wrap in backticks: `rank` |
Toujours entourer de backticks : `rank` |
| PDO named param used twice in one prepare | PDO param nommé répété deux fois dans un prepare | Use positional ? and execute([$val, $val]) |
Utiliser ? positionnels et execute([$val, $val]) |
CORS with credentials: 'include' |
CORS avec credentials: 'include' |
Exact origin whitelist + Access-Control-Allow-Credentials: true |
Whitelist d'origines exactes + Access-Control-Allow-Credentials: true |
| Stacked autocomplete listeners on replay | Listeners autocomplete empilés sur replay | Use init flag (_acInitDone) — mutate array in-place instead of re-binding |
Flag d'init (_acInitDone) — muter le tableau en place plutôt que rebinder |
window.onclick = fn overwrites previous handler |
window.onclick = fn écrase le handler précédent |
Always use window.addEventListener('click', fn) |
Toujours utiliser window.addEventListener('click', fn) |
| DST Paris not handled correctly | DST Paris mal géré | Intl.DateTimeFormat('fr-FR', { timeZone: 'Europe/Paris' }) |
Intl.DateTimeFormat('fr-FR', { timeZone: 'Europe/Paris' }) |
New PHP file in api/user/ or api/admin/ |
Nouveau fichier PHP dans api/user/ ou api/admin/ |
Each new .php file requires an explicit RewriteRule in .htaccess |
Chaque nouveau .php = nouvelle RewriteRule explicite dans .htaccess |
PersonaDLE est un projet fan-made. Pour toute décision d'architecture majeure, documenter dans PersonaDLE_Update_Documentation/PersonaDLE 2.0/.
PersonaDLE is a fan-made project. For any major architectural decision, document it in PersonaDLE_Update_Documentation/PersonaDLE 2.0/.